package com.yudean.esb.util;

import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.LoggerFactory;

import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

/**
 * 反射工具类
 *
 * @author : DL<duanlei@gdyd.com>
 * @version : 1.0
 */
public abstract class ReflectionUtils {
    private final static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ReflectionUtils.class);
    /**
     * CGLIB
     */
    public static final String CGLIB_CLASS_SEPARATOR = "$$";
    /**
     * 缓存方法
     */
    private static final Map<Class<?>, Method[]> METHODS_CACHEMAP = new HashMap<Class<?>, Method[]>();

    /**
     * 禁止实例化
     */
    private ReflectionUtils() {
    }

    /**
     * 返回指定类型所对应的primitive类型。包含String类
     * <p/>
     * fixed:paramter string type.
     *
     * @param clazz 要检查的类型
     * @return 如果指定类型为<code>null</code>或不是primitive类型的包装类，则返回<code>false</code>
     * ，否则返回<code>true</code>。
     */
    public static boolean isPrimitiveType(Class<?> clazz) {
        return clazz != null
                && (clazz.isPrimitive() || clazz.equals(Long.class)
                || clazz.equals(Integer.class)
                || clazz.equals(Short.class)
                || clazz.equals(Byte.class)
                || clazz.equals(Double.class)
                || clazz.equals(Float.class)
                || clazz.equals(Boolean.class)
                || clazz.equals(Character.class) || clazz
                .equals(String.class));

    }

    /**
     * 返回指定类型所对应的非primitive类型。
     *
     * @param clazz 要检查的类型
     * @return 如果指定类型为<code>null</code>，则返回<code>true</code>，如果是primitive类型，则返回
     * <code>false</code>。
     */
    public static boolean isNonPrimitiveType(Class<?> clazz) {
        return clazz == null || !clazz.isPrimitive()
                || !clazz.equals(long.class) && !clazz.equals(int.class)
                && !clazz.equals(short.class) && !clazz.equals(byte.class)
                && !clazz.equals(double.class) && !clazz.equals(float.class)
                && !clazz.equals(boolean.class) && !clazz.equals(char.class)
                && !clazz.equals(String.class);

    }

    /**
     * 反射 取值、设值,合并两个对象(Field same only )
     *
     * @param fromobj
     * @param toobj
     * @param fieldspec
     * @param <T>
     */
    public static <T> void copyProperties(T fromobj, T toobj,
                                          String... fieldspec) {
        for (String filename : fieldspec) {
            Object val = ReflectionUtils.invokeGetter(fromobj, filename);
            ReflectionUtils.invokeSetter(toobj, filename, val);
        }

    }

    /**
     * 调用Getter方法
     *
     * @param obj
     * @param propertyName
     * @return
     */
    public static Object invokeGetter(Object obj, String propertyName) {
        String getterMethodName = "get"
                + StringHelper.firstCharToUpperCase(propertyName);
        return invokeMethod(obj, getterMethodName, null, null);
    }

    /**
     * 调用Setter方法,不指定参数的类型
     *
     * @param obj
     * @param propertyName
     * @param value
     */
    public static void invokeSetter(Object obj, String propertyName,
                                    Object value) {
        invokeSetter(obj, propertyName, value, null);
    }

    /**
     * 调用Setter方法,指定参数的类型
     *
     * @param obj
     * @param propertyName
     * @param value
     * @param propertyType
     */
    public static void invokeSetter(Object obj, String propertyName,
                                    Object value, Class<?> propertyType) {
        propertyType = propertyType != null ? propertyType : value.getClass();
        String setterMethodName = "set"
                + StringHelper.firstCharToUpperCase(propertyName);
        invokeMethod(obj, setterMethodName, new Class<?>[]{propertyType},
                new Object[]{value});
    }

    /**
     * 直接调用对象方法，忽视private/protected修饰符
     *
     * @param obj
     * @param methodName
     * @param parameterTypes
     * @param args
     * @return
     */
    public static Object invokeMethod(final Object obj,
                                      final String methodName, final Class<?>[] parameterTypes,
                                      final Object[] args) {
        Method method = getAccessibleMethod(obj, methodName, parameterTypes);
        if (method == null) {
            throw new IllegalArgumentException("Could not find method ["
                    + methodName + "] on target [" + obj + "]");
        }
        try {
            return method.invoke(obj, args);
        } catch (Exception e) {
            LOGGER.error(e.getMessage());
        }
        return null;
    }

    /**
     * 循环向上转型，获取对象的DeclaredMethod,并强制设置为可访问 如向上转型到Object仍无法找到，返回null
     * <p/>
     * 用于方法需要被多次调用的情况，先使用本函数先取得Method,然后调用Method.invoke(Object obj,Object...
     * args)
     *
     * @param obj
     * @param methodName
     * @param parameterTypes
     * @return
     */
    public static Method getAccessibleMethod(final Object obj,
                                             final String methodName, final Class<?>... parameterTypes) {
        if (obj == null) {
            return null;
        }
        Class<?> superClass = obj.getClass();
        Class<Object> objClass = Object.class;
        for (; superClass != objClass; superClass = superClass.getSuperclass()) {
            Method method = null;
            try {
                method = superClass.getDeclaredMethod(methodName,
                        parameterTypes);
                method.setAccessible(true);
                return method;
            } catch (Exception e) {
                // Method不在当前类定义，继续向上转型
            }
        }
        return null;
    }

    /**
     * 不能确定方法是否包含参数时，通过方法名匹配获得方法
     *
     * @param obj
     * @param methodName
     * @return
     */
    public static Method obtainMethod(final Object obj, final String methodName) {
        Class<?> clazz = obj.getClass();
        Method[] methods = METHODS_CACHEMAP.get(clazz);
        if (methods == null) { // 尚未缓存
            methods = clazz.getDeclaredMethods();
            METHODS_CACHEMAP.put(clazz, methods);
        }
        for (Method method : methods) {
            if (method.getName().equals(methodName))
                return method;
        }
        return null;

    }

    /**
     * 直接读取对象属性值 忽视private/protected修饰符，不经过getter函数
     *
     * @param obj
     * @param fieldName
     * @return
     */
    public static Object getFieldValue(final Object obj,
                                       final String fieldName) {
        Field field = obtainAccessibleField(obj, fieldName);
        if (field == null) {
            throw new IllegalArgumentException("Devkit: could not find field ["
                    + fieldName + "] on target [" + obj + "]");
        }
        Object retval = null;
        try {
            retval = field.get(obj);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return retval;

    }

    /**
     * 直接设置对象属性值 忽视private/protected修饰符，不经过setter函数
     *
     * @param obj
     * @param fieldName
     * @param value
     */
    public static void setFieldValue(final Object obj, final String fieldName,
                                     final Object value) {
        Field field = obtainAccessibleField(obj, fieldName);
        if (field == null) {
            throw new IllegalArgumentException("Devkit: could not find field ["
                    + fieldName + "] on target [" + obj + "]");
        }
        try {
            field.set(obj, value);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
    }

    /**
     * 循环向上转型，获取对象的DeclaredField,并强制设为可访问 如向上转型Object仍无法找到，返回null
     *
     * @param obj
     * @param fieldName
     * @return
     */
    public static Field obtainAccessibleField(final Object obj,
                                              final String fieldName) {
        Class<?> superClass = obj.getClass();
        Class<Object> objClass = Object.class;
        for (; superClass != objClass; superClass = superClass.getSuperclass()) {
            try {
                Field field = superClass.getDeclaredField(fieldName);
                field.setAccessible(true);
                return field;
            } catch (Exception e) {
                //LOGGER.error(e.getMessage(), e);
            }
        }
        return null;
    }

    public static Field[] getAllFields(Object obj) {
        Field[] fields = new Field[]{};
        for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz
                .getSuperclass()) {
            fields = (Field[]) ArrayUtils.addAll(fields,
                    clazz.getDeclaredFields());
        }
        return fields;
    }

    /**
     * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. 如无法找到, 返回Object.class. eg. public UserDao
     * extends HibernateDao<User>
     *
     * @param clazz The class to introspect
     * @return the first generic declaration, or Object.class if cannot be
     * determined
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public static <T> Class<T> getSuperClassGenricType(final Class clazz) {
        return getSuperClassGenricType(clazz, 0);
    }

    /**
     * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. 如无法找到, 返回Object.class.
     * <p/>
     * 如public UserDao extends HibernateDao<User,Long>
     *
     * @param clazz clazz The class to introspect
     * @param index the Index of the generic ddeclaration,start from 0.
     * @return the index generic declaration, or Object.class if cannot be
     * determined
     */
    @SuppressWarnings("rawtypes")
    public static Class getSuperClassGenricType(final Class clazz,
                                                final int index) {

        Type genType = clazz.getGenericSuperclass();

        if (!(genType.getClass().isAssignableFrom(ParameterizedType.class))) {
            LOGGER.warn(clazz.getSimpleName()
                    + "'s superclass not ParameterizedType");
            return Object.class;
        }

        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

        if (index >= params.length || index < 0) {
            LOGGER.warn("Index: " + index + ", Size of "
                    + clazz.getSimpleName() + "'s Parameterized Type: "
                    + params.length);
            return Object.class;
        }
        if (!(params[index].getClass().isAssignableFrom(Class.class))) {
            LOGGER.warn(clazz.getSimpleName()
                    + " not set the actual class on superclass generic parameter");
            return Object.class;
        }

        return (Class) params[index];
    }

    /**
     * 对于被cglib AOP过的对象, 取得真实的Class类型.
     *
     * @param clazz
     * @return
     */
    public static Class<?> getUserClass(Class<?> clazz) {
        if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null && !Object.class.equals(superClass)) {
                return superClass;
            }
        }
        return clazz;
    }

    /**
     * 执行反射执行
     *
     * @param className 类型
     * @return 类的实例
     */
    @SuppressWarnings("rawtypes")
    public static Object instance(String className) {
        try {
            Class dialectCls = Class.forName(className);
            return dialectCls.newInstance();
        } catch (ClassNotFoundException e) {
            LOGGER.error("无法找到方言类", e);
            return null;
        } catch (InstantiationException e) {
            LOGGER.error("实例化方言错误", e);
            return null;
        } catch (IllegalAccessException e) {
            LOGGER.error("实例化方言错误", e);
            return null;
        }
    }

    /**
     * 将反射时的checked exception转换为unchecked exception.
     *
     * @param e
     * @return
     */
    public static RuntimeException convertReflectionExceptionToUnchecked(
            Exception e) {
        if (e instanceof IllegalAccessException
                || e instanceof IllegalArgumentException
                || e instanceof NoSuchMethodException) {
            return new IllegalArgumentException(e);
        } else if (e instanceof InvocationTargetException) {
            return new RuntimeException(
                    ((InvocationTargetException) e).getTargetException());
        } else if (e instanceof RuntimeException) {
            return (RuntimeException) e;
        }
        return new RuntimeException("Unexpected Checked Exception.", e);
    }
}

